﻿using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using ProtoBuf;

namespace TCG
{
	/// <summary>
	/// 预设数据库，加载预设、缓存预设等.
	/// </summary>

    public class GamePrefabDatabase : BaseAssetsDatabase<GameObject> 
	{
		/// <summary>
		/// 清理所有引用数为0的预设以释放内存空间。
		/// </summary>

		public override void UnloadUnusedAssets ()
		{
			Debug.Log ("GamePrefabDatabase: UnloadUnusedAssets");
			foreach (KeyValuePair<uint, int> _pair in m_zeroRefAssets)
			{
				uint assetId = _pair.Key;
				GameAsset<GameObject> prefabAsset = m_cachedAssets[assetId];
				Debug.Log ("* " + prefabAsset.ToString ());
				GameObject prefab = prefabAsset.asset;
				GameObject.DestroyImmediate (prefab, true);
				m_cachedAssets.Remove (assetId);

				PrefabData data = m_cachedAssetDepends[assetId];
				data.Unload ();
				m_cachedAssetDepends.Remove (assetId);
			}

			m_zeroRefAssets.Clear ();

		}

		/// <summary>
		/// 获得预设的{id}-{path}映射表文件的路径。
		/// </summary>

        protected override string GetMapFilePath()
        {
            return CommonTools.GetStreamingPath ("config/prefab_map");
        }

		/// <summary>
		/// 加载预设。
		/// </summary>

        protected override void DoLoad (AssetRequest request)
        {
			m_assetLoader.LoadAssetBundle (request, LoadCallback);
        }

		/// <summary>
		/// 预设加载后的回调。如果成功，还要读取预设关联的meta文件，以确定其依赖的材质信息，并依次加载所依赖的材质，
		/// 直到整个依赖关系链加载完成，本次预设的加载请求才算真正结束，方能调用OnLoadSuccess。
		/// </summary>

		void LoadCallback (AssetRequest request, AssetBundle assetBundle)
		{
			if (string.IsNullOrEmpty (request.error))
			{
				GameObject prefab = assetBundle.mainAsset as GameObject;
				assetBundle.Unload (false);

				// load metainfo
				FileStream stream = new FileStream (CommonTools.GetStreamingPath (GetAssetPath (request.assetId) + "_meta"), FileMode.Open);
				PrefabData data = Serializer.Deserialize<PrefabData> (stream);
				stream.Close ();

				if (data.renderDatas == null || data.renderDatas.Count == 0)
				{
					OnLoadSuccess (request.assetId, prefab);
					m_cachedAssetDepends.Add (request.assetId, data);
				}
				else
				{
					data.prefab = prefab;
					PrefabMaker pm = new PrefabMaker (request, data, MakePrefabCallback);
					m_dependencyRequesters.Add (request.assetId, pm);
					pm.MakePrefab ();
				}
			}
			else
			{
				OnLoadFail (request.assetId, request.error);
			}
		}

		/// <summary>
		/// 处理预设依赖关系链的回调，如果成功，则表示所有的依赖资源已加载完成，此时可以调用OnLoadSuccess。
		/// </summary>

		void MakePrefabCallback (AssetRequest request, GameObject prefab, PrefabData prefabData)
		{
			if (string.IsNullOrEmpty (request.error))
			{
				OnLoadSuccess (request.assetId, prefab);
				m_cachedAssetDepends.Add (request.assetId, prefabData);
			}
			else
			{
				OnLoadFail (request.assetId, request.error);
			}
			m_dependencyRequesters.Remove (request.assetId);
		}

		/// <summary>
		/// 处理预设的依赖资源链
		/// </summary>

		class PrefabMaker
		{
			public PrefabMaker (AssetRequest request, PrefabData prefabData, Action<AssetRequest, GameObject, PrefabData> callback)
			{
				m_request = request;
				m_prefabData = prefabData;
				m_callback = callback;
			}
			
			public void MakePrefab ()
			{
				IList<PrefabRenderData> renderDatas = m_prefabData.renderDatas;
				for (int i = 0, len = renderDatas.Count; i < len; ++i)
				{
					IList<uint> matIds = renderDatas[i].sharedMatIds;
					for (int j = 0, matLen = matIds.Count; j < matLen; ++ j)
					{
						new AssetRequest (matIds[j], AssetType.ProtoStream, OnMaterialResponse).Request<Material> ();
					}
				}
			}

			void OnMaterialResponse (AssetRequest request)
			{
				if (allMaterialsLoaded) return;

				IList<PrefabRenderData> renderDatas = m_prefabData.renderDatas;

				// 加载依赖链出错时，要将之前获得的资源引用减1
				if (!string.IsNullOrEmpty (request.error))
				{
					for (int i = 0, len = renderDatas.Count; i < len; ++i)
					{
						PrefabRenderData renderData = renderDatas[i];

						for (int j = 0, matLen = renderData.sharedMats.Count; j < matLen; j ++)
						{
							if (renderData.sharedMats[j] != null)
							{
								renderData.sharedMats[j].DecRef ();
							}
						}
					}

					m_request.error = request.error;
					m_callback (m_request, null, null);
					return;
				}


				allMaterialsLoaded = true;
				for (int i = 0, len = renderDatas.Count; i < len; ++ i)
				{
					PrefabRenderData renderData = renderDatas[i];
					renderData.TryMatrial (request.assetId, request.GetResultAsset<Material> ());
					allMaterialsLoaded = allMaterialsLoaded && renderData.completed;
				}

				if (allMaterialsLoaded)
				{
					m_prefabData.MakePrefab ();
					m_callback (m_request, m_prefabData.prefab, m_prefabData);
				}
			}

			bool allMaterialsLoaded { get; set; }

			AssetRequest m_request;
			PrefabData m_prefabData;
			Action<AssetRequest, GameObject, PrefabData> m_callback;
		}

		/// <summary>
		/// 当前正在处理的预设关系链列表。
		/// </summary>

		IDictionary<uint, PrefabMaker> m_dependencyRequesters = new Dictionary<uint, PrefabMaker> ();

		/// <summary>
		/// 和已缓存的预设资源一一对应，描述预设的依赖数据。
		/// </summary>

		IDictionary<uint, PrefabData> m_cachedAssetDepends = new Dictionary<uint, PrefabData> ();
	}
}
